﻿#region Imports
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Text;
using System.Xml.Serialization;
using CommonUtilities.Enums;
using CommonUtilities.Exceptions;
using CommonUtilities.Extensions;
#endregion

namespace CommonUtilities.Util
{
    /// <summary>
    /// Allows (de-)serialization of collection with formatted data
    /// </summary>
    public static class Serializer
    {
        /// <summary>
        /// Default object initialization for JSon deserialization
        /// </summary>
        const bool defObjInit = false;

        // RKD : clean-up
        ///// <summary>
        ///// Deserializes JSon data into specified structure
        ///// </summary>
        ///// <typeparam name="TSource">Type of item in the collection</typeparam>
        ///// <param name="jsonData">JSon data as JsonArray</param>
        ///// <param name="initObj">Initializes the object, else default value</param>
        ///// <returns>Data parsed into the specified class structure</returns>
        //public static TSource DeserializeJson<TSource>(JsonArray jsonData, bool initObj = defObjInit)
        //{
        //    return DeserializeJson<TSource>(!jsonData.IsNull() ? jsonData.ToString() : null, initObj);
        //}

        #region Generic
        /// <summary>
        /// Deserializes data from specified format into specified structure
        /// </summary>
        /// <typeparam name="TSource">Type of item in the collection</typeparam>
        /// <param name="format">Format of data</param>
        /// <param name="value">Formatted data as string</param>
        /// <param name="initObj">Initializes the object, else default value</param>
        /// <returns>Data parsed into the specified class structure</returns>
        public static TSource DeSerializeFromFormattedData<TSource>(this string value, eSerializationFormat format, bool initObj = defObjInit)
        {
            if (!value.IsNull())
            {
                switch (format)
                {
                    case eSerializationFormat.JSon:
                        return DeserializeFromJson<TSource>(value, initObj);
                    case eSerializationFormat.Xml:
                        return DeserializeFromXml<TSource>(value, initObj);
                    default:
                        throw new SerializationTypeNotMatchedException();
                }
            }
            else
                //return initObj ? new TSource() : default(TSource);
                return default(TSource);
        }

        /// <summary>
        /// Serializes data into specified format
        /// </summary>
        /// <typeparam name="TSource">Type of item in the collection</typeparam>
        /// <param name="value">Collection containing data</param>
        /// <param name="format">Format of data</param>
        /// <returns>Serialized data in specified format</returns>
        public static string SerializeToFormattedData<TSource>(this TSource value, eSerializationFormat format)
        {
            // RKD : clean-up
            // if( Utilities.IsType<T1> (typeof(T1, eMatchType.Exact))
            //eSerializationFormat e=(eSerializationFormat)T1;
            //else return;

            // RKD : clean-up
            // Type 'DirectoryAnalyzer.Models.DInfo' with data contract name 
            // 'DInfo:http://schemas.datacontract.org/2004/07/DirectoryAnalyzer.Models' is not expected. 
            // Consider using a DataContractResolver or add any types not known statically to the list 
            // of known types - for example, by using the KnownTypeAttribute attribute or 
            // by adding them to the list of known types passed to DataContractSerializer.
            //if (!TypeDescriptor.GetAttributes(typeof(TSource))[typeof(SerializableAttribute)].IsNull())
            //    throw new SerializationException("Type '" + typeof(TSource) + "' is not serializable.\n"
            //        + "Please add " + typeof(SerializableAttribute) + " to the '" + typeof(TSource) + "' type parameter.        ");

            try
            {
                switch (format)
                {
                    case eSerializationFormat.JSon:
                        return SerializeToJson<TSource>(value);
                    case eSerializationFormat.Xml:
                        return SerializeToXml<TSource>(value);
                    default:
                        throw new SerializationTypeNotMatchedException();
                }
            }
            catch (SerializationException ex)
            {
                throw new SerializationException("Type '" + typeof(TSource) + "' is not serializable.\n"
                    + "Please add " + typeof(SerializableAttribute) + " to the '" + typeof(TSource) + "' type parameter.        ", ex);
            }
            catch (Exception)
            {
                throw;
            }
        }

        // RKD test later
        //public static string SerializeToFormattedData<TSource>(this IEnumerable<TSource> value, eSerializationFormat format)
        //{
        //    return value.SerializeToFormattedData<IEnumerable<TSource>>(format);
        //}
        #endregion

        #region JSON
        /// <summary>
        /// Deserializes data from JSon format into specified structure
        /// </summary>
        /// <seealso cref="http://msdn.microsoft.com/en-us/library/system.runtime.serialization.json.datacontractjsonserializer.aspx"/>
        /// <typeparam name="TSource">Type of item in the collection</typeparam>
        /// <param name="sJSON">JSon data as string</param>
        /// <param name="initObj">Initializes the object, else default value</param>
        /// <returns>Data parsed into the specified class structure</returns>
        private static TSource DeserializeFromJson<TSource>(this string sJSON, bool initObj = defObjInit)
        {
            if (!sJSON.IsNull())
            {
                MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(sJSON));
                DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(TSource));
                TSource result = (TSource)serializer.ReadObject(ms);
                ms.Close();

                return result;
            }
            else
                //return initObj ? new TSource() : default(TSource);
                return default(TSource);
        }

        /// <summary>
        /// Serializes data into JSon format
        /// </summary>
        /// <typeparam name="TSource">Type of item in the collection</typeparam>
        /// <param name="value">Collection containing data</param>
        /// <returns>Serialized data in JSon format</returns>
        private static string SerializeToJson<TSource>(this TSource value)
        {
            //DataContractJsonSerializer json = new DataContractJsonSerializer(typeof(IEnumerable<TSource>));
            DataContractJsonSerializer json = new DataContractJsonSerializer(typeof(TSource));
            MemoryStream memoryStream = new MemoryStream();
            json.WriteObject(memoryStream, value);
            string sJSON = Encoding.UTF8.GetString(memoryStream.ToArray(), 0, (int)memoryStream.Length);
            memoryStream.Close();

            return sJSON;
        }

        private static string SerializeToJson<TSource>(this IEnumerable<TSource> value)
        {
            return value.SerializeToJson<IEnumerable<TSource>>();
        }
        #endregion

        #region XML
        /// <summary>
        /// Deserializes data from XML format into specified structure
        /// </summary>
        /// <typeparam name="TSource">Type of item in the collection</typeparam>
        /// <param name="sXML">Xml data as string</param>
        /// <param name="initObj">Initializes the object, else default value</param>
        /// <returns>Data parsed into the specified class structure</returns>
        private static TSource DeserializeFromXml<TSource>(this string sXML, bool initObj = defObjInit)
        {
            if (!sXML.IsNull())
            {
                MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(sXML));
                XmlSerializer serializer = new XmlSerializer(typeof(TSource));
                TSource result = (TSource)serializer.Deserialize(ms);
                ms.Close();

                return result;
            }
            else
                //return initObj ? new TSource() : default(TSource);
                return default(TSource);
        }

        /// <summary>
        /// Serializes data into XML format
        /// </summary>
        /// <typeparam name="TSource">Type of item in the collection</typeparam>
        /// <param name="value">Collection containing data</param>
        /// <returns>Serialized data in XML format</returns>
        private static string SerializeToXml<TSource>(this TSource value)
        {
            XmlSerializer xml = new XmlSerializer(typeof(TSource));
            MemoryStream memoryStream = new MemoryStream();
            xml.Serialize(memoryStream, value);
            string sXML = Encoding.UTF8.GetString(memoryStream.ToArray(), 0, (int)memoryStream.Length);
            memoryStream.Close();

            return sXML;
        }

        private static string SerializeToXml<TSource>(this IEnumerable<TSource> value)
        {
            return value.SerializeToXml<IEnumerable<TSource>>();
        }
        #endregion
    }
}

/* // http://stackoverflow.com/questions/8722126/c-sharp-xml-serialization-deserialization
public class Movie
{
    [XmlElement("MovieName")]
    public string Title
    { get; set; }

    [XmlElement("MovieRating")]
    public float Rating
    { get; set; }

    [XmlElement("MovieReleaseDate")]
    public DateTime ReleaseDate
    { get; set; }
}
*/
